package com.hotacorp.opencloud.userservice.service.impl;

import java.io.Serializable;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hotacorp.opencloud.common.authcommon.SysUserBean;
import com.hotacorp.opencloud.common.mybatisplus.PageData;
import com.hotacorp.opencloud.common.mybatisplus.Query;
import com.hotacorp.opencloud.userservice.dao.SysMenuDao;
import com.hotacorp.opencloud.userservice.dao.SysRoleMenuDao;
import com.hotacorp.opencloud.userservice.dao.SysUserDao;
import com.hotacorp.opencloud.userservice.dao.SysUserRoleDao;
import com.hotacorp.opencloud.userservice.entity.SysMenuEntity;
import com.hotacorp.opencloud.userservice.entity.SysRoleMenuEntity;
import com.hotacorp.opencloud.userservice.entity.SysUserEntity;
import com.hotacorp.opencloud.userservice.entity.SysUserRoleEntity;
import com.hotacorp.opencloud.userservice.service.SysUserRoleService;
import com.hotacorp.opencloud.userservice.service.SysUserService;
import com.hotacorp.opencloud.userservice.util.UserConstants;

/**
 * 用户管理接口实现
 * @author lwg
 *
 */
@Service("sysUserService")
public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUserEntity> implements SysUserService {
	@Autowired
	private SysUserRoleDao sysUserRoleDao;
	@Autowired
	private SysRoleMenuDao sysRoleMenuDao;
	@Autowired
	private SysMenuDao sysMenuDao;
	@Autowired
	private StringRedisTemplate redisTemplate;
	@Autowired
	private SysUserRoleService sysUserRoleService;
	private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    @Override
    public PageData queryPage(Map<String, Object> params) {
    	QueryWrapper<SysUserEntity> qurey = new QueryWrapper<SysUserEntity>();
    	qurey.eq("tenantid", params.get("tenantid"));
    	if (StringUtils.isNotBlank((String)params.get("username"))) qurey.eq("username", params.get("username"));
        IPage<SysUserEntity> page = this.page(
                new Query<SysUserEntity>().getPage(params),
                qurey
        );
        page.getRecords().forEach(action->{
        	action.setPassword(null);
        });
        return new PageData(page);
    }
    
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void saveUser(SysUserEntity user, Long tenantid) {
		user.setCreatetime(new Date());
		user.setUpdatetime(new Date());
		user.setPassword(passwordEncoder.encode(user.getPassword()));
		user.setTenantid(tenantid);
		this.save(user);
		
		//保存用户与角色关系
		sysUserRoleService.saveOrUpdate(user.getId(), user.getRoleIdList(), tenantid);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void update(SysUserEntity user, Long tenantid) {
		SysUserEntity sdfs = this.getOne((new QueryWrapper<SysUserEntity>()).eq("id", user.getId()).eq("tenantid", tenantid));
		if (sdfs == null) {
			throw new RuntimeException("非法操作数据");
		}
		user.setUpdatetime(new Date());
		if(StringUtils.isBlank(user.getPassword())){
			user.setPassword(null);
		}else{
			user.setPassword(passwordEncoder.encode(user.getPassword()));
		}
		this.updateById(user);
		
		//保存用户与角色关系
		sysUserRoleService.saveOrUpdate(user.getId(), user.getRoleIdList(), tenantid);
		redisTemplate.delete(UserConstants.USER_NAME_KEY + ":" + tenantid + ":" +user.getUsername());
		redisTemplate.delete(UserConstants.PHONE_KEY + ":" + tenantid + ":" +user.getPhone());
	}

	@Override
	public SysUserBean loadUserByUsername(Long tenantid, String username) {
		String dddd = redisTemplate.boundValueOps(UserConstants.USER_NAME_KEY + ":" + tenantid + ":" +username).get();
		if (dddd == null) {
			QueryWrapper<SysUserEntity> wrapper = new QueryWrapper<SysUserEntity>();
			wrapper.eq("username", username);
			wrapper.eq("tenantid", tenantid);
			SysUserEntity entity = this.baseMapper.selectOne(wrapper);
			if (entity != null) {
				SysUserBean retBean = new SysUserBean();
				BeanUtils.copyProperties(entity, retBean);
				retBean.setAuthoritys(getAuthoritys(entity.getId(), entity.getUsername()));
				String sss = JSON.toJSONString(retBean);
				redisTemplate.boundValueOps(UserConstants.USER_NAME_KEY + ":" + tenantid + ":" +username).set(sss, Duration.ofMinutes(10));
				return retBean;
			} else {
				redisTemplate.boundValueOps(UserConstants.USER_NAME_KEY + ":" + tenantid + ":" +username).set("", Duration.ofMinutes(10 +(new Random()).nextInt(10)));
				return null;
			}
		} else if ("".equals(dddd)) {
			return null;
		} else {
			redisTemplate.boundValueOps(UserConstants.USER_NAME_KEY + ":" + tenantid + ":" +username).expire(10, TimeUnit.MINUTES);
			SysUserBean retBean = JSON.parseObject(dddd, SysUserBean.class);
			return retBean;
		}
	}

	@Override
	public SysUserBean loadUserByPhone(Long tenantid, String phone) {
		String dddd = redisTemplate.boundValueOps(UserConstants.PHONE_KEY + ":" + tenantid + ":" +phone).get();
		if (dddd == null) {
			QueryWrapper<SysUserEntity> wrapper = new QueryWrapper<SysUserEntity>();
			wrapper.eq("phone", phone);
			wrapper.eq("tenantid", tenantid);
			SysUserEntity entity = this.baseMapper.selectOne(wrapper);
			if (entity != null) {
				SysUserBean retBean = new SysUserBean();
				BeanUtils.copyProperties(entity, retBean);
				retBean.setAuthoritys(getAuthoritys(entity.getId(), entity.getUsername()));
				String sss = JSON.toJSONString(retBean);
				redisTemplate.boundValueOps(UserConstants.PHONE_KEY + ":" + tenantid + ":" +phone).set(sss, Duration.ofMinutes(10));
				return retBean;
			} else {
				redisTemplate.boundValueOps(UserConstants.PHONE_KEY + ":" + tenantid + ":" +phone).set("", Duration.ofMinutes(10 +(new Random()).nextInt(10)));
				return null;
			}
		} else if ("".equals(dddd)) {
			return null;
		} else {
			redisTemplate.boundValueOps(UserConstants.PHONE_KEY + ":" + tenantid + ":" +phone).expire(10, TimeUnit.MINUTES);
			SysUserBean retBean = JSON.parseObject(dddd, SysUserBean.class);
			return retBean;
		}
	}

	/**
	 * 取某个用户的权限
	 * @param id 用户ID
	 * @return
	 */
	private Set<String> getAuthoritys(Long id, String username) {
		Set<String> authoritys = new HashSet<>();
		if (id == 1) {//超级系统管理员，直接取所有权限
			List<SysMenuEntity> menus = sysMenuDao.selectList((new QueryWrapper<SysMenuEntity>()));
			menus.forEach(menu->{
				String permString = menu.getPerms();
				String[] dddStrings = StringUtils.split(permString, ",");
				if (dddStrings != null && dddStrings.length != 0) {
					authoritys.addAll(Arrays.asList(dddStrings));
				}
			});
			
			return authoritys;
		} else if ("admin".equals(username)) {//租户管理员
			List<SysMenuEntity> menus = sysMenuDao.selectList((new QueryWrapper<SysMenuEntity>()).eq("adminused", 0));
			menus.forEach(menu->{
				String permString = menu.getPerms();
				String[] dddStrings = StringUtils.split(permString, ",");
				if (dddStrings != null && dddStrings.length != 0) {
					authoritys.addAll(Arrays.asList(dddStrings));
				}
			});			
			return authoritys;
		} else {
			List<SysUserRoleEntity> userroles = sysUserRoleDao.selectList((new QueryWrapper<SysUserRoleEntity>()).eq("user_id", id));
			Set<Long> roleidSet = new HashSet<>();
			userroles.forEach(userrole->{roleidSet.add(userrole.getRoleId());});
			if (roleidSet.size() > 0) {
				List<SysRoleMenuEntity> rolemenus = sysRoleMenuDao.selectList((new QueryWrapper<SysRoleMenuEntity>()).in("role_id", roleidSet));
				Set<Long> menuidSet = new HashSet<>();
				rolemenus.forEach(rolemenu->{menuidSet.add(rolemenu.getMenuId());});
				if (menuidSet.size() > 0) {
					List<SysMenuEntity> menus = sysMenuDao.selectList((new QueryWrapper<SysMenuEntity>()).in("menu_id", menuidSet));
					menus.forEach(menu->{
						String permString = menu.getPerms();
						String[] dddStrings = StringUtils.split(permString, ",");
						if (dddStrings != null && dddStrings.length != 0) {
							authoritys.addAll(Arrays.asList(dddStrings));
						}
					});
					
				}
			}
			return authoritys;
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean removeByIds(Collection<? extends Serializable> idList, Long tenantid) {
		
		QueryWrapper<SysUserEntity> wrap = new QueryWrapper<SysUserEntity>();
		wrap.eq("tenantid", tenantid);
		wrap.in("id", idList);
		List<SysUserEntity> ttt = baseMapper.selectList(wrap);
		if (ttt == null || ttt.size() == 0) {
			throw new RuntimeException("非法操作数据");
		} else {
			Set<Long> ssss = ttt.stream().map(entity->{return entity.getId();}).collect(Collectors.toSet());
			QueryWrapper<SysUserRoleEntity> wrap1 = new QueryWrapper<SysUserRoleEntity>();
			wrap1.eq("tenantid", tenantid);
			wrap1.in("user_id", idList);
			sysUserRoleDao.delete(wrap1);
			int a = baseMapper.deleteBatchIds(ssss);
			if (a > 0) {
				ttt.forEach(action->{
					redisTemplate.delete(UserConstants.USER_NAME_KEY + ":" + tenantid + ":" + action.getUsername());
					redisTemplate.delete(UserConstants.PHONE_KEY + ":" + tenantid + ":" + action.getPhone());
				});
				return true;
			} else {
				return false;
			}
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean updatePassword(Long userid, String password, String newPassword) {
		SysUserEntity oii = this.baseMapper.selectById(userid);
		if (oii != null) {
			if (passwordEncoder.matches(password, oii.getPassword())) {
				oii.setUpdatetime(new Date());
				oii.setPassword(passwordEncoder.encode(newPassword));
				boolean ddd = this.updateById(oii);
				if (ddd) {
					redisTemplate.delete(UserConstants.USER_NAME_KEY + ":" + oii.getTenantid() + ":" + oii.getUsername());
					redisTemplate.delete(UserConstants.PHONE_KEY + ":" + oii.getTenantid() + ":" + oii.getPhone());
					return true;
				}
			}
		}
		return false;
	}

}